/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief Implement the error handling.
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#include <stdio.h>
#include <stdlib.h> 
#include <stdarg.h>
#include <string.h>   // strerror()
#include <stdbool.h>

#include "../include/amcc_log.h"
#include "../include/amcc_error.h"

const char* error_errno_desc() {
    return strerror(errno);
}

static int  gErrCounter = 0; // counter of errors , increased in every report action

int error_get_count() { return gErrCounter; }


typedef struct t_error_format {
    EnumErrorCode  code;
    const char * format_string;
} t_error_format;


// Format strings for all errors.
// All elements MUST be in the same order as EnumErrorCode
static t_error_format error_formats[] = {

#define  ERRCODE_DEFN(N, ENUM, DESC)   { ENUM,  DESC }
#define  ERRCODE_DEF( N, ENUM, DESC)   { ENUM,  DESC }

    #include "../meta/_errname_desc.inc"

#undef   ERRCODE_DEFN
#undef   ERRCODE_DEF

};

static int nErrorFormats = sizeof(error_formats) / sizeof(error_formats[0]);

static int  cmp_format_key(const void* a, const void* b){
    const EnumErrorCode * pa = (const EnumErrorCode*)a;
    const EnumErrorCode * pb = (const EnumErrorCode*)b;
    return (int)(*pa - *pb);
}

static const char* find_err_desc_format(EnumErrorCode ecode) {
    t_error_format k ;
    k.code = ecode;
    t_error_format* item = bsearch(&k, error_formats, nErrorFormats,
                                   sizeof(error_formats[0]), cmp_format_key);
    return (NULL == item) ? NULL : item->format_string;
}

#define FMTSTR_ECODE    "ERROR-%03d: "
#define SZ_ERRFMT       500


static void error_report_INNER(const char* srcFile, int srcLine,
                    bool isSysErr, const char * fileName,
                    int line, int col, EnumErrorCode ecode,
                    va_list  arg_list)
{
    ++ gErrCounter;
    const char* fmtDefine = find_err_desc_format(ecode);
    if ( NULL == fmtDefine ) {
        const char* no_fmt = FMTSTR_ECODE
                            "FOUND BUG - \"no description for error %d\""
                            ", please define it in \"_errname_desc.inc\"";
        if(NULL != amcc_get_errorFile())
            fprintf(amcc_get_errorFile(), no_fmt , ecode, ecode);
        log_error(no_fmt, ecode, ecode);
        return;
    }

    char  finalFormat[SZ_ERRFMT];
    if ( isSysErr ) {
        snprintf(finalFormat, sizeof(finalFormat)-1,
                 FMTSTR_ECODE "[SYS] %s",
                 ecode, fmtDefine);
    } else {
        snprintf(finalFormat, sizeof(finalFormat)-1,
                 FMTSTR_ECODE "[%s] [%d:%d] %s",
                 ecode, fileName, line, col, fmtDefine);
    }

    if (NULL != amcc_get_errorFile()) {
        vfprintf(amcc_get_errorFile(), finalFormat, arg_list);
        fprintf (amcc_get_errorFile(), "\n");
    }

    //log_verror( finalFormat, arg_list );
    log_implement_valist(LOG_ERROR, srcFile, srcLine,
                         finalFormat, arg_list);
}


/**
 * @brief report an error of a system operation.
 *
 * @param srcFile source file path of caller
 * @param srcLine source line of caller
 * @param ecode  error code
 * @param ...    other parameters for format string
 *
 * @see 'error_formats[]' in file 'error.c'
 */
void error_report_system_impl(const char* srcFile, int srcLine,
                         EnumErrorCode ecode, ...)
{
    va_list  arg_list;
    va_start( arg_list, ecode );
    error_report_INNER(srcFile, srcLine,
                       true, NULL, 0, 0, ecode, arg_list);
    va_end( arg_list );
}

/**
 * @brief reports an error in an input file
 *
 * @param srcFile source file path of caller
 * @param srcLine source line of callse
 * @param fileName  file name of error
 * @param line   line number of error
 * @param col    column number of error
 * @param ecode  error code
 * @param ...    other parameters for format string
 *
 * @see 'error_formats[]' in srcfile 'error.c'
 */
void error_report_input_impl(const char* srcFile, int srcLine,
                        const char * fileName, int line, int col,
                        EnumErrorCode ecode, ...)
{
    va_list  arg_list;
    va_start( arg_list, ecode );
    error_report_INNER(srcFile, srcLine,
                       false, fileName, line, col, ecode, arg_list);
    va_end( arg_list );
}


/**
 * @brief report a file operation failure such as create/open, read, write a file.
 *
 * @param srcFile source file path of caller
 * @param srcLine source line of caller
 * @param fileName  file path of error
 * @param ecode     error code
 * @param ...       other parameters for format string
 * @see 'error_formats[]' in file 'error.c'
 */
void error_report_fileop_impl(const char* srcFile, int srcLine,
                         const char* fileName,
                         EnumErrorCode ecode, ...)
{
    va_list  arg_list;
    va_start( arg_list, ecode );
    error_report_INNER(srcFile, srcLine,
                       false, fileName, 0, 0, ecode, arg_list);
    va_end( arg_list );
}


